本篇内容和源码均参考 UE5。

# Delegate 委托

委托是 Unreal 对于 C++ 函数的一种封装。在 Unreal 中,委托也被分成以下几种类型:

  • Delegate:简单委托,支持绑定单个执行函数。
  • MulticaseDelegate:多播委托,支持绑定一个或多个执行函数。
  • Event:事件,带有访问权限控制的多播委托,需要设置可以触发该多播委托的友元类。
  • DynamicDelegate:动态委托,支持动态绑定单个执行函数。
  • DynamicMulticaseDelegate:动态多播委托,支持动态绑定一个或多个执行函数。
  • SparseDynamicDelegate:稀疏动态委托,可以理解为另一种存储形式的 DynamicMulticaseDelegate。

# Delegate 简单委托

Delegate 通过宏定义进行声明: DECLARE_DELEGATE_XXXParams ,宏展开后内容如下:

// example:
DECLARE_DELEGATE_TwoParams(FOnQuaternionCommitted, FQuat, ETextCommit::Type);
// 展开结果:
typedef TDelegate<void(FQuat, ETextCommit::Type)> FOnQuaternionCommitted;;

# TDelegate

Delegate 声明实际上是对 TDelegate 模板类的声明。TDelegate 最终会继承 UserPolicy 中的 FDelegateExtras

UserPolicy 是外部定义的模板类,默认为 FDefaultDelegateUserPolicyFDelegateExtras 默认为 FDelegateBase

// TDelegate 本身的定义:
template <typename DelegateSignature, typename UserPolicy = FDefaultDelegateUserPolicy>
class TDelegate
{
	// ...
};
template <typename InRetValType, typename... ParamTypes, typename UserPolicy>
class TDelegate<InRetValType(ParamTypes...), UserPolicy> : public TDelegateBase<UserPolicy>
{
	// ...  
};
template <typename UserPolicy>
class TDelegateBase : public UserPolicy::FDelegateExtras
{
    // ...
}
struct FDefaultDelegateUserPolicy
{
	using FDelegateInstanceExtras  = IDelegateInstance;
	using FDelegateExtras          = FDelegateBase;
	using FMulticastDelegateExtras = TMulticastDelegateBase<FDefaultDelegateUserPolicy>;
};

FDelegateBase 提供了对 Delegate 的内存管理,所有 TDelegate 的创建销毁底层都是基于 FDelegateBase

# Delegate Instance

Unreal 基于成员函数的类型及函数特性对 Delegate 又划分出了多个不同的实例:

  • TBaseRawMethodDelegateInstance:任意 C++ 类型的成员函数委托,对象通过指针进行存储,如果对象被销毁,执行不安全。
  • TBaseSPMethodDelegateInstance:任意 C++ 类型的成员函数委托,通过一个线程安全的共享指针来存储委托所绑定的对象,执行是安全的。
  • TBaseUFunctionDelegateInstanceUObject 对象类型 UFunction 函数委托,基于反射进行查找因此必须是 UFunction 函数,构造时可以只提供函数名,提供弱引用指针绑定 UObject 因此也是安全的。
  • TBaseUObjectMethodDelegateInstanceUObject 对象类型成员函数委托,构造时需要提供函数指针,对 UObject 也是弱引用,因此执行是安全的。
  • TBaseFunctorDelegateInstance:任意类型的函数委托,存储函数指针,执行是安全的。
  • TWeakBaseFunctorDelegateInstance:任意类型的成员函数委托,保存的是对象的弱引用,执行是安全的。
  • TBaseStaticDelegateInstance:静态 C++ 函数指针,存储函数指针,执行是安全的。

image-20221029100815344

所有 Delegate Instance 最终都继承自 UserPolicy 中的 FDelegateInstanceExtras,并各自实现了函数绑定规则。

# Delegate Instance 实例化

TDelegate 为所有类型的 Delegate Instance 提供了实例化的接口:

/**
* Static: Creates a raw C++ pointer global function delegate
*/
template <typename... VarTypes>
UE_NODISCARD inline static TDelegate<RetValType(ParamTypes...), UserPolicy> CreateStatic(typename TIdentity<RetValType (*)(ParamTypes..., VarTypes...)>::Type InFunc, VarTypes... Vars)
{
	TDelegate<RetValType(ParamTypes...), UserPolicy> Result;
	TBaseStaticDelegateInstance<FuncType, UserPolicy, VarTypes...>::Create(Result, InFunc, Vars...);
	return Result;
}
// ...

所有类型的 Delegate Instance 都在自己的 Create 接口中通过 FDelegateBase 提供的 new 函数实现内存分配和实例化(自己创建自己了属于是😃

FORCEINLINE static void Create(FDelegateBase& Base, FFuncPtr InFunc, VarTypes... Vars)
{
	new (Base) UnwrappedThisType(InFunc, Vars...);
}
class FDelegateBase
{
private:
    FDelegateAllocatorType::ForElementType<FAlignedInlineDelegateType> DelegateAllocator;
	int32 DelegateSize;
    friend void* operator new(size_t Size, FDelegateBase& Base);
    void* Allocate(int32 Size)
	{
		if (IDelegateInstance* CurrentInstance = GetDelegateInstanceProtected())
		{
			CurrentInstance->~IDelegateInstance();
		}
		int32 NewDelegateSize = FMath::DivideAndRoundUp(Size, (int32)sizeof(FAlignedInlineDelegateType));
		if (DelegateSize != NewDelegateSize)
		{
			DelegateAllocator.ResizeAllocation(0, NewDelegateSize, sizeof(FAlignedInlineDelegateType));
			DelegateSize = NewDelegateSize;
		}
		return DelegateAllocator.GetAllocation();
	}
};
inline void* operator new(size_t Size, FDelegateBase& Base)
{
	return Base.Allocate((int32)Size);
}

new 会调用 Base.Allocate 分配给 Delegate Instance 内存空间,并在该空间内构造 Delegate Instance,而由于该空间被 FDelegateBase 中的 DelegateAllocator 所管理,因此 Create 最终返回的是 TDelegate 模板实例。

这样,就通过 TDelegate 静态函数构造出了个新的 TDelegate 实例,TDelegate 实例中还包含了一个 Delegate Instance 并绑定了一个该类型的执行函数上。

# MulticaseDelegate 多播委托

同理,MulticaseDelegate 的宏定义为: DECLARE_MULTICAST_DELEGATE_XXXParams ,宏展开后内容如下:

// example:
DECLARE_MULTICAST_DELEGATE_TwoParams(FNewItemEvent, const FVisualLoggerDBRow&, int32);
// 展开结果:
typedef TMulticastDelegate<void(const FVisualLoggerDBRow&, int32)> FNewItemEvent;;

# TMulticastDelegate

MulticaseDelegate 声明实际上是对 TMulticastDelegate 模板类的声明。而 TMulticastDelegate 最终会继承 UserPolicy 中的 FMulticastDelegateExtras

默认为 TMulticastDelegateBase

template <typename... ParamTypes, typename UserPolicy>
class TMulticastDelegate<void(ParamTypes...), UserPolicy> : public UserPolicy::FMulticastDelegateExtras
{
	// ...
};
using FMulticastDelegateExtras = TMulticastDelegateBase<FDefaultDelegateUserPolicy>;

TMulticastDelegateBase 本质上是 TDelegateBase 的数组,多播(Broadcast)其实是对所有已注册的 TDelegateBase 的倒序执行(ExecuteIfSafe):

class TMulticastDelegateBase
{
private:
	/** Holds the collection of delegate instances to invoke. */
	InvocationListType InvocationList;
	/** Holds a lock counter for the invocation list. */
	mutable int32 InvocationListLockCount;
};
template <typename... ParamTypes, typename UserPolicy>
class TMulticastDelegate<void(ParamTypes...), UserPolicy> : public UserPolicy::FMulticastDelegateExtras
{
	void Broadcast(ParamTypes... Params) const
	{
		bool NeedsCompaction = false;
		Super::LockInvocationList();
		{
			const InvocationListType& LocalInvocationList = Super::GetInvocationList();
             // call bound functions in reverse order, so we ignore any instances that may be added by callees
			for (int32 InvocationListIndex = LocalInvocationList.Num() - 1; InvocationListIndex >= 0; --InvocationListIndex)
			{
				const FDelegate& DelegateBase = (const FDelegate&)LocalInvocationList[InvocationListIndex];
				IDelegateInstance* DelegateInstanceInterface = Super::GetDelegateInstanceProtectedHelper(DelegateBase);
				if (DelegateInstanceInterface == nullptr || !((DelegateInstanceInterfaceType*)DelegateInstanceInterface)->ExecuteIfSafe(Params...))
				{
					NeedsCompaction = true;
				}
			}
		}
		Super::UnlockInvocationList();
		if (NeedsCompaction)
		{
			const_cast<TMulticastDelegate*>(this)->CompactInvocationList();
		}
	}
};

# Event 事件

Event 本质上就是一个 MulticaseDelegate 多播委托。唯一的区别在于 Event 需要指定调用的类型。通过友元类控制,并非所有对象都可以触发 Event。

// example:
DECLARE_EVENT_TwoParams(UEngine, FLevelActorOuterChangedEvent, AActor*, UObject*);
// 展开结果:
class FLevelActorOuterChangedEvent : public TMulticastDelegate<void(AActor*, UObject*)> { friend class UEngine; };;

# DynamicDelegate 动态委托

相比于简单委托,动态委托采用了另一套实现体系 ——TBaseDynamicDelegate

// example:
DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnItemSelected, UUserWidget*, Widget, bool, Selected);
// 展开结果:
FID_Engine_Plugins_Experimental_CommonUI_Source_CommonUI_Public_CommonUITypes_h_176_DELEGATE
class FOnItemSelected : public TBaseDynamicDelegate<FWeakObjectPtr, void, UUserWidget *, bool> {
public:
    FOnItemSelected() {}
    explicit FOnItemSelected(const TScriptDelegate<> &InScriptDelegate) : TBaseDynamicDelegate<FWeakObjectPtr, void, UUserWidget *, bool>(InScriptDelegate) {}
    inline void Execute(UUserWidget *InParam1, bool InParam2) const {
        checkSlow(IsBound());
        FOnItemSelected_DelegateWrapper(*this, InParam1, InParam2);
    }
    inline bool ExecuteIfBound(UUserWidget *InParam1, bool InParam2) const {
        if (IsBound()) {
            FOnItemSelected_DelegateWrapper(*this, InParam1, InParam2);
            return true;
        }
        return false;
    }
};;

可以看到 ExecuteExecuteIfBound 都调用了 FOnItemSelected_DelegateWrapper,但并没有 FOnItemSelected_DelegateWrapper 的声明和定义

答案其实在 FID_Engine_Plugins_Experimental_CommonUI_Source_CommonUI_Public_CommonUITypes_h_176_DELEGATE 中, FID_Engine_Plugins_Experimental_CommonUI_Source_CommonUI_Public_CommonUITypes_h_176_DELEGATE 本身是一个宏定义,它的定义规则通过 Unreal 的 UHT 生成在 XXX.generated.h 文件内:

#define FID_Engine_Plugins_Experimental_CommonUI_Source_CommonUI_Public_CommonUITypes_h_176_DELEGATE \
struct _Script_CommonUI_eventOnItemSelected_Parms \
{ \
	UUserWidget* Widget; \
	bool Selected; \
}; \
static inline void FOnItemSelected_DelegateWrapper(const FScriptDelegate& OnItemSelected, UUserWidget* Widget, bool Selected) \
{ \
	_Script_CommonUI_eventOnItemSelected_Parms Parms; \
	Parms.Widget=Widget; \
	Parms.Selected=Selected ? true : false; \
	OnItemSelected.ProcessDelegate<UObject>(&Parms); \
}

因此最终展开结果应该是这样:

struct _Script_CommonUI_eventOnItemSelected_Parms {
    UUserWidget *Widget;
    bool Selected;
};
static inline void FOnItemSelected_DelegateWrapper(const FScriptDelegate &OnItemSelected, UUserWidget *Widget, bool Selected) {
    _Script_CommonUI_eventOnItemSelected_Parms Parms;
    Parms.Widget = Widget;
    Parms.Selected = Selected ? true : false;
    OnItemSelected.ProcessDelegate<UObject>(&Parms);
}
class FOnItemSelected : public TBaseDynamicDelegate<FWeakObjectPtr, void, UUserWidget *, bool> {
public:
    FOnItemSelected() {}
    explicit FOnItemSelected(const TScriptDelegate<> &InScriptDelegate) : TBaseDynamicDelegate<FWeakObjectPtr, void, UUserWidget *, bool>(InScriptDelegate) {}
    inline void Execute(UUserWidget *InParam1, bool InParam2) const {
        checkSlow(IsBound());
        FOnItemSelected_DelegateWrapper(*this, InParam1, InParam2);
    }
    inline bool ExecuteIfBound(UUserWidget *InParam1, bool InParam2) const {
        if (IsBound()) {
            FOnItemSelected_DelegateWrapper(*this, InParam1, InParam2);
            return true;
        }
        return false;
    }
};;

# TScriptDelegate

TScriptDelegate 是所有 DynamicDelegate 的基类,提供绑定 / 解绑 / 执行等基础接口。相比于简单委托在绑定完成后,只能通过 Delegate Instance 销毁重建的形式来切换绑定对象;TScriptDelegate 采用了更优雅的动态绑定机制,即通过对象实例指针 && 函数名 利用反射机制的绑定流程。然而反射机制的实现,依赖于 UObject 对象关联的 UClass 中包含的元数据,因此动态委托的绑定函数也必须是 UFunction 类型。

template <typename TWeakPtr, typename RetValType, typename... ParamTypes>
class TBaseDynamicDelegate : public TScriptDelegate<TWeakPtr>
{
    // ...
};
template <typename TWeakPtr = FWeakObjectPtr>
class TScriptDelegate
{
	/**
	 * Binds a UFunction to this delegate.
	 *
	 * @param InObject The object to call the function on.
	 * @param InFunctionName The name of the function to call.
	 */
	void BindUFunction( UObject* InObject, const FName& InFunctionName )
	{
		Object = InObject;
		FunctionName = InFunctionName;
	}
    
    /**
	 * Unbinds this delegate
	 */
	void Unbind()
	{
		Object = nullptr;
		FunctionName = NAME_None;
	}
    
     /**
	 * Executes a delegate by calling the named function on the object bound to the delegate.  You should
	 * always first verify that the delegate is safe to execute by calling IsBound() before calling this function.
	 * In general, you should never call this function directly.  Instead, call Execute() on a derived class.
	 *
	 * @param	Parameters		Parameter structure
	 */
	//CORE_API void ProcessDelegate(void* Parameters) const;
	template <class UObjectTemplate>
	void ProcessDelegate( void* Parameters ) const
	{
		// sure IsBound() returns true before calling ProcessDelegate()!
		UObjectTemplate* ObjectPtr = static_cast< UObjectTemplate* >( Object.Get() );	// Down-cast
		checkSlow( IsValid(ObjectPtr) );
		// Object *must* implement the specified function
		UFunction* Function = ObjectPtr->FindFunctionChecked( FunctionName );
		// Execute the delegate!
		ObjectPtr->ProcessEvent(Function, Parameters);
	}
    // ...
};
UFunction* UObject::FindFunctionChecked( FName InName ) const
{
	UFunction* Result = FindFunction(InName);
	if (Result == NULL)
	{
		UE_LOG(LogScriptCore, Fatal, TEXT("Failed to find function %s in %s"), *InName.ToString(), *GetFullName());
	}
	return Result;
}
UFunction* UObject::FindFunction( FName InName ) const
{
	return GetClass()->FindFunctionByName(InName);
}

# DynamicMulticaseDelegate 动态多播委托

该委托可以动态绑定一组成员函数,对于支持编辑器形式 (需要动态增删) 的事件调用非常的有效(尤其是蓝图

// example:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnBeforePopup, FString, URL, FString, Frame);
// 展开结果:
struct WebBrowser_eventOnBeforePopup_Parms {
    FString URL;
    FString Frame;
};
static inline void FOnBeforePopup_DelegateWrapper(const FMulticastScriptDelegate &OnBeforePopup, const FString &URL, const FString &Frame) {
    WebBrowser_eventOnBeforePopup_Parms Parms;
    Parms.URL = URL;
    Parms.Frame = Frame;
    OnBeforePopup.ProcessMulticastDelegate<UObject>(&Parms);
}
class FOnBeforePopup : public TBaseDynamicMulticastDelegate<FWeakObjectPtr, void, FString, FString> {
public:
    FOnBeforePopup() {}
    explicit FOnBeforePopup(const TMulticastScriptDelegate<> &InMulticastScriptDelegate) : TBaseDynamicMulticastDelegate<FWeakObjectPtr, void, FString, FString>(InMulticastScriptDelegate) {}
    void Broadcast(FString InParam1, FString InParam2) const { FOnBeforePopup_DelegateWrapper(*this, InParam1, InParam2); }
};;

# TMulticastScriptDelegate

TMulticastScriptDelegate 是所有 DynamicMulticaseDelegate 的基类,负责管理和维护 TScriptDelegate 的数组

image-20221027104606040

class TMulticastScriptDelegate
{
     /**
	 * Executes a multi-cast delegate by calling all functions on objects bound to the delegate.  Always
	 * safe to call, even if when no objects are bound, or if objects have expired.  In general, you should
	 * never call this function directly.  Instead, call Broadcast() on a derived class.
	 *
	 * @param	Params				Parameter structure
	 */
	template <class UObjectTemplate>
	void ProcessMulticastDelegate(void* Parameters) const
	{
		if( InvocationList.Num() > 0 )
		{
			// Create a copy of the invocation list, just in case the list is modified by one of the callbacks during the broadcast
			typedef TArray< TScriptDelegate<TWeakPtr>, TInlineAllocator< 4 > > FInlineInvocationList;
			FInlineInvocationList InvocationListCopy = FInlineInvocationList(InvocationList);
	
			// Invoke each bound function
			for( typename FInlineInvocationList::TConstIterator FunctionIt( InvocationListCopy ); FunctionIt; ++FunctionIt )
			{
				if( FunctionIt->IsBound() )
				{
					// Invoke this delegate!
					FunctionIt->template ProcessDelegate<UObjectTemplate>(Parameters);
				}
				else if ( FunctionIt->IsCompactable() )
				{
					// Function couldn't be executed, so remove it.  Note that because the original list could have been modified by one of the callbacks, we have to search for the function to remove here.
					RemoveInternal( *FunctionIt );
				}
			}
		}
	}
    
	typedef TArray< TScriptDelegate<TWeakPtr> > FInvocationList;
	/** Ordered list functions to invoke when the Broadcast function is called */
	mutable FInvocationList InvocationList;		// Mutable so that we can housekeep list even with 'const' broadcasts
}

# SparseDynamicDelegate 稀疏动态委托

// example:
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_TwoParams(FActorOnClickedSignature, AActor, OnClicked, AActor*, TouchedActor, FKey, ButtonPressed);
// 展开结果:
struct _Script_Engine_eventActorOnClickedSignature_Parms {
    AActor *TouchedActor;
    FKey ButtonPressed;
};
static inline void FActorOnClickedSignature_DelegateWrapper(const FMulticastScriptDelegate &ActorOnClickedSignature, AActor *TouchedActor, FKey ButtonPressed) {
    _Script_Engine_eventActorOnClickedSignature_Parms Parms;
    Parms.TouchedActor = TouchedActor;
    Parms.ButtonPressed = ButtonPressed;
    ActorOnClickedSignature.ProcessMulticastDelegate<UObject>(&Parms);
}
class FActorOnClickedSignature_MCSignature : public TBaseDynamicMulticastDelegate<FWeakObjectPtr, void, AActor *, FKey> {
public:
    FActorOnClickedSignature_MCSignature() {}
    explicit FActorOnClickedSignature_MCSignature(const TMulticastScriptDelegate<> &InMulticastScriptDelegate) : TBaseDynamicMulticastDelegate<FWeakObjectPtr, void, AActor *, FKey>(
            InMulticastScriptDelegate) {}
    void Broadcast(AActor *InParam1, FKey InParam2) const { FActorOnClickedSignature_DelegateWrapper(*this, InParam1, InParam2); }
};
struct FActorOnClickedSignatureInfoGetter {
    static const char *GetDelegateName() { return "OnClicked"; }
    template<typename T>
    static size_t GetDelegateOffset() { return offsetof(T, OnClicked); }
};
struct FActorOnClickedSignature : public TSparseDynamicDelegate<FActorOnClickedSignature_MCSignature, AActor, FActorOnClickedSignatureInfoGetter> {
};;

SparseDynamicDelegate 宏展开相对复杂一些,主要内容可以分为两部分:

  • 继承自 TBaseDynamicMulticastDelegateFActorOnClickedSignature_MCSignature 类,实现了基本的动态多播委托逻辑,但命名上做了改动,没有正常暴露给外部使用。
  • 继承自 TSparseDynamicDelegate<FActorOnClickedSignature_MCSignature...>FActorOnClickedSignature 类,重新实现了 TBaseDynamicMulticastDelegate 中操作委托数组的各项接口,重定向修改到了 FSparseDelegateStorage。开发者实际上是通过 FActorOnClickedSignature 间接操作 FActorOnClickedSignature_MCSignature。

image-20221027113233720

struct FSparseDelegate
{
	/**
	* Adds a function delegate to this multi-cast delegate's invocation list if a delegate with the same signature
	* doesn't already exist in the invocation list
	*
	* @param	DelegateOwner	UObject that owns the resolved sparse delegate
	* @param	DelegateName	Name of the resolved sparse delegate
	* @param	InDelegate		Delegate to bind to the sparse delegate
	* 
	* NOTE:  Only call this function from blueprint sparse delegate infrastructure on a resolved generic FScriptDelegate pointer.
	*        Generally from C++ you should call AddUnique() directly.
	*/
	void __Internal_AddUnique(const UObject* DelegateOwner, FName DelegateName, FScriptDelegate InDelegate)
	{
		bIsBound |= FSparseDelegateStorage::AddUnique(DelegateOwner, DelegateName, MoveTemp(InDelegate));
	}
    
    // ...
};

# FSparseDelegateStorage

FSparseDelegateStorage 可以理解成一个维护全局动态多播委托的单例,它的所有成员变量 / 函数都是 Static。通过一个全局 map 来记录 UObject 中所有 FMulticastScriptDelegate 的弱引用,而 FMulticastScriptDelegate 中又存储了多个 FScriptDelegate

顺带一提 FScriptDelegateFMulticastScriptDelegate 分别是 TScriptDelegateTMulticastScriptDelegate 的特化版本... 这里经常容易搞混。

// Typedef script delegates for convenience.
typedef TScriptDelegate<> FScriptDelegate;
typedef TMulticastScriptDelegate<> FMulticastScriptDelegate;

通过 TSparseDynamicDelegateFMulticastScriptDelegate 添加 FScriptDelegate 时,会先把 FMulticastScriptDelegate 注册到 FSparseDelegateStorage 全局 map 中,再把 FScriptDelegate 添加到 FMulticastScriptDelegate 容器内。

注意:DelegateOwner 只是持有操作 FMulticastScriptDelegate 的 TSparseDynamicDelegate 对象,FSparseDelegateStorage 才负责存储 FMulticastScriptDelegate 和所有注册的 FScriptDelegate;DelegateOwner 通过 TSparseDynamicDelegateFSparseDelegateStorage 中的数据进行修改。

// 添加
bool FSparseDelegateStorage::Add(const UObject* DelegateOwner, const FName DelegateName, FScriptDelegate Delegate)
{
	bool bDelegateWasBound = false;
	if (Delegate.IsBound())
	{
		FScopeLock SparseDelegateMapLock(&SparseDelegateMapCritical);
		if (SparseDelegates.Num() == 0)
		{
			SparseDelegateObjectListener.EnableListener();
		}
		FSparseDelegateMap& DelegateMap = SparseDelegates.FindOrAdd(DelegateOwner);
		TSharedPtr<FMulticastScriptDelegate>& MulticastDelegate = DelegateMap.FindOrAdd(DelegateName);
		if (!MulticastDelegate.IsValid())
		{
			MulticastDelegate = MakeShared<FMulticastScriptDelegate>();
		}
		// 实际操作的是 FSparseDelegateStorage 中创建 or 已存在的 FMulticastScriptDelegate
		MulticastDelegate->Add(MoveTemp(Delegate));
		bDelegateWasBound = true;
	}
	return bDelegateWasBound;
}
// 查询
TSharedPtr<FMulticastScriptDelegate> FSparseDelegateStorage::GetSharedMulticastDelegate(const UObject* DelegateOwner, const FName DelegateName)
{
	FScopeLock SparseDelegateMapLock(&SparseDelegateMapCritical);
	TSharedPtr<FMulticastScriptDelegate> Result;
	if (FSparseDelegateMap* DelegateMap = SparseDelegates.Find(DelegateOwner))
	{
		if (TSharedPtr<FMulticastScriptDelegate>* MulticastDelegatePtr = DelegateMap->Find(DelegateName))
		{
			Result = *MulticastDelegatePtr;
		}
	}
	return Result;
}

# 总结

Unreal 提供了多种委托,其中「Delegate 」用法灵活,支持各种类型的函数绑定,执行效率也较,对于有返回值的函数调用时不太安全;「MulticaseDelegate 」在原有基础上扩充了一对多的绑定关系;「Event 」又在 MulticaseDelegate 的基础上通过 friend class 约束了调用者;「DynamicDelegate 」相比于 Delegate,舍弃了更多函数类型的支持,降低了执行效率,但能够动态绑定且调用更加的安全;「DynamicMulticaseDelegate 」同样在原有基础上扩充了一对多的绑定关系;「SparseDynamicDelegate」又在 DynamicMulticaseDelegate 基础上重定向了委托的存储位置,通过 FSparseDelegateStorage 全局 map 集中管理。

更新于 阅读次数

请我[恰饭]~( ̄▽ ̄)~*

鑫酱(●'◡'●) 微信支付

微信支付

鑫酱(●'◡'●) 支付宝

支付宝